# apps/imports/tests.py
import json
from datetime import timedelta
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse, resolve
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from unittest.mock import patch
from apps.imports.serializers import ImportLogSerializer

from .models import ImportLog
from .views import ImportLogViewSet
from ..users.models import QBUser

User = get_user_model()

# 👇 扩展 get_full_name，增加 fallback 到 username
def get_full_name_fallback(self):
    full_name = f"{self.first_name} {self.last_name}".strip()
    return full_name or self.username

# -------------------------------
# 1. 模型测试
# -------------------------------
class ImportLogModelTest(TestCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # 临时替换 get_full_name
        QBUser.add_to_class('get_full_name', get_full_name_fallback)

    def setUp(self):
        self.QBuser = QBUser.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='pass123'
        )

    def test_create_import_log(self):
        log = ImportLog.objects.create(
            file_name='test.xlsx',
            file_size=1024,
            total_count=100,
            success_count=95,
            importer=self.QBuser
        )
        log.save()
        self.assertEqual(log.file_name, 'test.xlsx')
        self.assertEqual(log.fail_count, 5)  # 100 - 95
        self.assertEqual(log.status, 'pending')
        self.assertEqual(str(log), 'test.xlsx')

    def test_fail_count_calculation(self):
        log = ImportLog(
            file_name='test.xlsx',
            file_size=512,
            total_count=50,
            success_count=30,
            importer=self.QBuser
        )
        log.save()
        self.assertEqual(log.fail_count, 20)

    def test_status_display(self):
        log = ImportLog(
            file_name='test.xlsx',
            file_size=100,
            total_count=10,
            importer=self.QBuser,
            status='completed',
            import_type='scheduled'
        )
        log.save()
        self.assertEqual(log.get_status_display(), '完成')
        self.assertEqual(log.get_import_type_display(), '定时')

    def test_index_creation(self):
        """验证数据库索引是否正确定义"""
        indexes = [idx.fields[0] for idx in ImportLog._meta.indexes]
        self.assertIn('importer', indexes)
        self.assertIn('status', indexes)
        self.assertIn('created_at', indexes)


# -------------------------------
# 2. 序列化器测试
# -------------------------------
class ImportLogSerializerTest(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # 临时替换 get_full_name
        User.add_to_class('get_full_name', get_full_name_fallback)

    def setUp(self):
        self.QBuser = QBUser.objects.create_user(username='testuser', password='pass123')

    def test_serializer_output(self):
        log = ImportLog.objects.create(
            file_name='upload.xlsx',
            file_size=2048,
            total_count=200,
            success_count=180,
            importer=self.QBuser,
            status='completed',
            started_at=timezone.now() - timedelta(minutes=2),
            completed_at=timezone.now()
        )

        # ✅ 正确方式：直接使用导入的序列化器
        serializer = ImportLogSerializer(log)
        data = serializer.data

        self.assertEqual(data['file_name'], 'upload.xlsx')
        self.assertEqual(data['success_count'], 180)
        self.assertEqual(data['status_label'], '完成')
        self.assertEqual(data['import_type_label'], '手动')
        self.assertEqual(data['importer_name'], 'testuser')  # 假设 get_full_name 返回 username

    def test_validation_success_count_exceeds_total(self):
        serializer = ImportLogSerializer(data={
            'file_name': 'test.xlsx',
            'file_size': 100,
            'total_count': 50,
            'success_count': 60,
            'importer': self.user.id
        })
        self.assertFalse(serializer.is_valid())
        self.assertIn('success_count', serializer.errors)
        self.assertEqual(
            serializer.errors['success_count'][0],
            '成功导入数不能大于总记录数。'
        )


# -------------------------------
# 3. 视图测试 (APITestCase)
# -------------------------------
class ImportLogViewSetTest(APITestCase):
    def setUp(self):
        # 创建用户
        self.QBuser = QBUser.objects.create_user(
            username='user',
            password='pass123',
            email='testuser@example.com',
            first_name='Normal',
            last_name='User'
        )
        self.admin = QBUser.objects.create_superuser(
            username='admin',
            password='admin123',
            email='admin@example.com'
        )

        # 创建测试数据
        self.log1 = ImportLog.objects.create(
            file_name='import1.xlsx',
            file_size=1024,
            total_count=100,
            success_count=0,
            importer=self.QBuser,
            created_at=timezone.now() - timedelta(days=1)
        )
        self.log2 = ImportLog.objects.create(
            file_name='import2.xlsx',
            file_size=2048,
            total_count=200,
            success_count=150,
            importer=self.admin,
            status='completed',
            created_at=timezone.now()
        )

        # API 客户端
        self.client = APIClient()
        self.QBuser = QBUser.objects.create_user(username='testuser', password='pass')
        self.client.force_login(self.QBuser)  # 避免 session 认证开销

        # URL 别名（根据 router basename）
        self.list_url = reverse('import-log-list')
        self.detail_url = lambda pk: reverse('import-log-detail', args=[pk])
        self.mark_processing_url = lambda pk: f"{self.detail_url(pk)}mark-processing/"
        self.mark_completed_url = lambda pk: f"{self.detail_url(pk)}mark-completed/"
        self.mark_failed_url = lambda pk: f"{self.detail_url(pk)}mark-failed/"
        self.stats_url = reverse('import-log-stats')
        self.my_latest_url = reverse('import-log-my-latest')

    def test_validation_success_count_exceeds_total(self):
        serializer = ImportLogSerializer(data={
            'file_name': 'test.xlsx',
            'file_size': 100,
            'total_count': 50,
            'success_count': 60,
            'importer': self.QBuser.id
        })
        self.assertFalse(serializer.is_valid())
        self.assertIn('success_count', serializer.errors)
        self.assertEqual(
            serializer.errors['success_count'][0],
            '成功导入数不能大于总记录数。'
        )
    def test_list_requires_auth(self):
        self.client.force_authenticate(user=self.admin)
        self.client.logout()
        response = self.client.get(self.list_url)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_list_returns_only_user_logs_for_non_admin(self):
        self.client.force_authenticate(user=self.QBuser)
        response = self.client.get(self.list_url)       # 修复：添加缺失的请求发送
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['file_name'], 'import1.xlsx')

    def test_admin_sees_all_logs(self):
        self.client.force_authenticate(user=self.admin)
        self.client.login(username='admin', password='admin123')
        response = self.client.get(self.list_url)
        self.assertEqual(len(response.data['results']), 2)

    def test_create_import_log(self):
        # 直接获取权限并添加
        self.QBuser.user_permissions.clear()  # 可选：先清空
        self.QBuser.user_permissions.add(
            Permission.objects.get(codename='add_importlog')
        )

        self.client.force_authenticate(user=self.QBuser)

        data = {
            'file_name': 'new_import.csv',
            'file_size': 512,
            'total_count': 50,
            'import_type': 'manual'
        }

        response = self.client.post(self.list_url, data)

        print("响应数据:", response.data)  # 调试用

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['importer'], self.QBuser.id)
        self.assertEqual(response.data['file_name'], 'new_import.csv')
    def test_mark_processing_success(self):
        #self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        url = self.mark_processing_url(self.log1.id)
        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], 'processing')
        self.assertIsNotNone(response.data['started_at'])

    def test_mark_processing_invalid_status(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        # 先标记为 completed
        self.log1.status = 'completed'
        self.log1.save()
        url = self.mark_processing_url(self.log1.id)
        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('error', response.data)
        self.assertEqual(response.data['error'], '只有“等待”状态的日志才能标记为处理中。')

    def test_mark_completed_success(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        # 👇 打印 log1 的当前状态
        print("Before update -> total_count:", self.log1.total_count, "success_count:", self.log1.success_count)
        self.log1.status = 'processing'
        self.log1.started_at = timezone.now() - timedelta(minutes=1)
        self.log1.save()
        # 👇 打印 log1 的当前状态
        print("Before update -> total_count:", self.log1.total_count, "success_count:", self.log1.success_count)
        self.log1.refresh_from_db()
        print("After update -> total_count:", self.log1.total_count, "success_count:", self.log1.success_count)
        url = self.mark_completed_url(self.log1.id)
        data = {'success_count': 95}
        response = self.client.post(url, data)
        print("Response data:", response.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], 'completed')
        self.assertEqual(response.data['success_count'], 95)
        print("Response data:", response.data)
        self.assertEqual(response.data['fail_count'], 5)  # 100 - 95
        self.assertIsNotNone(response.data['completed_at'])

    def test_mark_completed_invalid_status(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        url = self.mark_completed_url(self.log1.id)  # still pending
        response = self.client.post(url, {'success_count': 50})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('error', response.data)
        self.assertEqual(response.data['error'], '只有“处理中”状态的日志才能标记为完成。')

    def test_mark_failed_success(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        url = self.mark_failed_url(self.log1.id)
        data = {'error_log': 'File format error.'}
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], 'failed')
        self.assertEqual(response.data['error_log'], 'File format error.')
        self.assertIsNotNone(response.data['completed_at'])

    @patch('rest_framework.permissions.DjangoModelPermissions.has_permission')
    def test_stats_cached_response(self, mock_perm):
        mock_perm.return_value = True

        self.client.force_authenticate(user=self.admin)
        # 创建测试数据
        ImportLog.objects.create(
            importer=self.QBuser,
            file_name='test.xlsx',
            total_count=100,
            success_count=90,
            fail_count=10,
            file_size=1024,  # ✅ 加上 file_size
            status='completed'
        )

        # 第一次请求：你的业务逻辑用了 3 次聚合查询
        with self.assertNumQueries(3):
            # 👇 打印 stats_url
            print("stats_url:", self.stats_url)
            response1 = self.client.get(self.stats_url)
            self.assertEqual(response1.status_code, 200)

        # 第二次请求：应该从缓存读取，0 查询
        with self.assertNumQueries(0):
            response2 = self.client.get(self.stats_url)
            self.assertEqual(response2.status_code, 200)

        self.assertEqual(response1.data, response2.data)
    def test_my_latest_no_record(self):
        new_user = QBUser.objects.create_user(username='newuser', password='123', email='newuser@example.com')
        self.client.force_authenticate(user=new_user)

        response = self.client.get(self.my_latest_url)
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_search_functionality(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)

        response = self.client.get(self.list_url, {'search': 'import1'})
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['file_name'], 'import1.xlsx')

    def test_ordering(self):
        self.client.login(username='admin', password='admin123')
        self.client.force_authenticate(user=self.admin)
        response = self.client.get(self.list_url, {'ordering': 'created_at'})
        results = response.data['results']
        self.assertEqual(results[0]['id'], self.log1.id)  # 更早创建

        response = self.client.get(self.list_url, {'ordering': '-created_at'})
        results = response.data['results']
        self.assertEqual(results[0]['id'], self.log2.id)  # 最近创建


# -------------------------------
# 4. 权限测试 (DjangoModelPermissions)
# -------------------------------
class ImportLogPermissionTest(APITestCase):
    def setUp(self):
        self.QBuser = QBUser.objects.create_user(username='user', password='pass123')
        self.client = APIClient()

    def test_user_without_view_permission_cannot_list(self):
        self.QBuser.user_permissions.clear()
        self.QBuser.save()  # 触发信号和缓存更新

        print("📋 用户权限:", self.QBuser.get_all_permissions())
        assert not self.QBuser.has_perm('imports.view_importlog')

        self.client.force_authenticate(user=self.QBuser)

        url = reverse('import-log-list')
        print("🌐 请求 URL:", url)

        response = self.client.get(url)
        print("❌ 状态码:", response.status_code)
        print("❌ 响应:", response.data)

        self.assertEqual(response.status_code, 403)  # 现在应该通过

    def test_user_with_view_permission_can_list_own_logs(self):
        # 赋予 view 权限
        from django.contrib.auth.models import Permission
        from django.contrib.contenttypes.models import ContentType
        ct = ContentType.objects.get_for_model(ImportLog)
        perm = Permission.objects.get_or_create(
            codename='view-import-log',
            content_type=ct,
            defaults={'name': 'Can view import log'}
        )[0]
        self.QBuser.user_permissions.add(perm)
        self.QBuser.refresh_from_db()

        # 创建属于该用户的数据
        ImportLog.objects.create(
            file_name='mylog.xlsx',
            file_size=100,
            total_count=10,
            importer=self.QBuser
        )

        self.client.login(username='user', password='pass123')
        self.client.force_authenticate(user=self.QBuser)
        response = self.client.get(reverse('import-log-list'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
